package org.windowkit.toolkit.widget.table;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.ddevil.data.ChangeTracker;
import org.ddevil.data.Data;
import org.ddevil.data.DisplayDescriptor;
import org.ddevil.data.ChangeTracker.Change;
import org.ddevil.data.set.DataSet;
import org.ddevil.data.util.DataSetUtils;
import org.windowkit.toolkit.event.EventDispatcher;
import org.windowkit.toolkit.util.LayoutUtils;
import org.windowkit.toolkit.widget.table.event.DataMoveEvent;
import org.windowkit.toolkit.widget.table.event.DataMoveListener;
import org.windowkit.toolkit.widget.table.event.DataMoveEvent.DataMoveType;

/**
 * This is an updated version of eveyone's favorite component, the two table
 * field.
 *
 * @author Eric Lundin
 */

public class TwoTablePane extends JComponent {

	private static final long serialVersionUID = 6982425416068391619L;

	RecordTablePane tblApplicable;

	RecordTablePane tblAvailable;

	String compID;

	JButton cmdAdd;

	JButton cmdRemove;

	JButton moveAllLeft;

	JButton moveAllRight;

	ChangeTracker tracker = new ChangeTracker();

	DataMoveListener dataMoveListener;

	EventDispatcher eventDispatcher = new EventDispatcher(this);

	public TwoTablePane() {
		initComponents();
		addListeners();
		layoutComponents();
	}

	private void initComponents() {
		tblApplicable = new RecordTablePane();
		tblAvailable = new RecordTablePane();

		tblApplicable.setShowNextDataButton(false);
		tblAvailable.setShowNextDataButton(false);

		cmdAdd = new JButton();
		cmdAdd.setText("Add >>");

		cmdRemove = new JButton();
		cmdRemove.setText("<< Remove");

		tblApplicable
				.setSelectionType(TableSelectionType.MULTIPLE_INTERVAL_SELECTION);
		tblAvailable
				.setSelectionType(TableSelectionType.MULTIPLE_INTERVAL_SELECTION);

	}

	public void setVisisbleRowCount(final int rowCount) {
		tblApplicable.setVisibleRowCount(rowCount);
		tblAvailable.setVisibleRowCount(rowCount);
	}

	protected void addListeners() {
		tblApplicable.addListSelectionListener(eventDispatcher);
		tblAvailable.addListSelectionListener(eventDispatcher);
		cmdAdd.addActionListener(new ActionListener() {
			@SuppressWarnings("unused")
			public void actionPerformed(final ActionEvent e) {
				addSelectedItem();
			}
		});
		cmdRemove.addActionListener(new ActionListener() {
			@SuppressWarnings("unused")
			public void actionPerformed(final ActionEvent e) {
				removeSelectedItem();
			}
		});

		tblAvailable.addListSelectionListener(new ListSelectionListener() {

			public void valueChanged(final ListSelectionEvent e) {
				boolean dataSelected = tblAvailable.isDataSelected();
				cmdAdd.setEnabled(false);
				cmdRemove.setEnabled(false);
				if (dataSelected) {
					tblApplicable.clearSelection();
					cmdAdd.setEnabled(dataSelected);
				}
				dataSelected = tblApplicable.isDataSelected();
				if (dataSelected) {
					tblAvailable.clearSelection();
					cmdRemove.setEnabled(dataSelected);
				}
			}
		});

		tblApplicable.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(final ListSelectionEvent e) {
				boolean dataSelected = tblApplicable.isDataSelected();
				cmdAdd.setEnabled(false);
				cmdRemove.setEnabled(false);
				if (dataSelected) {
					tblAvailable.clearSelection();
					cmdRemove.setEnabled(dataSelected);
				}

				dataSelected = tblAvailable.isDataSelected();
				if (dataSelected) {
					tblApplicable.clearSelection();
					cmdAdd.setEnabled(dataSelected);
				}
			}
		});

		tblApplicable.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(final MouseEvent me) {
				if (me.getClickCount() == 2 && tblApplicable.isDataSelected()) {
					removeSelectedItem();
				}
			}
		});
		tblAvailable.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(final MouseEvent me) {
				if (me.getClickCount() == 2 && tblAvailable.isDataSelected()) {
					addSelectedItem();
				}
			}
		});
	}

	/**
	 *
	 */
	protected void removeSelectedItem() {
		DataSet records = tblApplicable.getSelectedRecords();
		moveSelectedItems(tblApplicable, tblAvailable);
		for (int i = 0; i < records.size(); i++) {
			Data rec = records.exportRecord(i);
			tracker.itemRemoved(rec);
			fireDataRemoved(rec);
		}
	}

	public List<Change> getChanges() {
		return tracker.getChanges();
	}

	public boolean hasChanges() {
		return tracker.hasChanges();
	}

	public void clearChanges() {
		tracker.clear();
	}

	/**
	 *
	 */
	protected void addSelectedItem() {
		DataSet records = tblAvailable.getSelectedRecords();
		moveSelectedItems(tblAvailable, tblApplicable);
		for (int i = 0; i < records.size(); i++) {
			Data rec = records.exportRecord(i);
			tracker.itemAdded(rec);
			fireDataAdded(rec);
		}
	}

	public void clearData() {
		tblApplicable.clearTableData();
		tblAvailable.clearTableData();
	}

	public DataSet getSelectedRecApplic() {
		return tblApplicable.getSelectedRecords();
	}

	public DataSet getSelectedRecAvail() {
		return tblAvailable.getSelectedRecords();
	}

	public void addListSelectionListener(final ListSelectionListener lsl) {
		eventDispatcher.addListSelectionListener(lsl);
	}

	public boolean isDataSelectedApplic() {
		return tblApplicable.isDataSelected();
	}

	public boolean isDataSelectedAvail() {
		return tblAvailable.isDataSelected();
	}

	public void clearApplicableSelection() {
		tblApplicable.clearSelection();
	}

	public void clearAvailableSelection() {
		tblAvailable.clearSelection();
	}

	public void setAddEnabled(final boolean enabled) {
		cmdAdd.setEnabled(enabled);
	}

	public void setRemoveEnabled(final boolean enabled) {
		cmdRemove.setEnabled(enabled);
	}

	/**
	 * If the given record exists in the available table add it to the
	 * applicable table. This does NOT fire an event but will be picked up by
	 * the change tracker
	 *
	 * @param atts
	 */
	public void addRecord(final Data record) {
		RecordTableModel model = tblAvailable.getModel();
		for (int i = 0; i < model.getDataRowCount(); i++) {
			Data currentRec = model.getRecord(i);
			if (DataSetUtils.attribsEqual(record, currentRec)) {
				tblApplicable.addRecord(currentRec);
				tblAvailable.removeRecord(i);
				tracker.itemAdded(currentRec);
			}
		}
	}

	/**
	 * If the record exists in the applicable table move it to the available
	 * table. This does NOT fire an event but will be picked up by the change
	 * tracker
	 */
	public void removeRecord(final Data record) {
		RecordTableModel model = tblApplicable.getModel();
		for (int i = 0; i < model.getDataRowCount(); i++) {
			Data currentRec = model.getRecord(i);
			if (DataSetUtils.attribsEqual(record, currentRec)) {
				tblAvailable.addRecord(currentRec);
				tblApplicable.removeRecord(i);
				tracker.itemRemoved(currentRec);
			}
		}
	}

	public void setColumnComparator(final String dbId, final Comparator c) {
		tblApplicable.setColumnComparator(dbId, c);
		tblAvailable.setColumnComparator(dbId, c);
	}

	void moveSelectedItems(final RecordTablePane source, final RecordTablePane dest) {
		ArrayList<Integer> selectedRowInidices = source.getSelectedRows();
		DataSet selectedRows = source.getSelectedRecords();
		dest.addRecords(selectedRows);
		for (int i = selectedRowInidices.size() - 1; i >= 0; i--) {
			source.removeRow(selectedRowInidices.get(i));
		}
	}

	private void layoutComponents() {
		JPanel p = new JPanel(new GridBagLayout());
		GridBagConstraints gbc = LayoutUtils.createConstraintsFillBoth();
		gbc.insets = new Insets(0, 0, 0, 2);
		p.add(tblAvailable, gbc);
		gbc = LayoutUtils.createConstraintsFillY();
		gbc.insets = new Insets(0, 0, 0, 0);
		p.add(createButtonPanel(), gbc);
		gbc = LayoutUtils.createConstraintsFillBoth();
		gbc.insets = new Insets(0, 2, 0, 0);
		p.add(tblApplicable, gbc);
		setLayout(new GridBagLayout());
		add(p, LayoutUtils.createConstraintsFillBoth());
	}

	private JComponent createButtonPanel() {
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridBagLayout());
		GridBagConstraints gbc = LayoutUtils.createConstraintsFillX();
		gbc.gridwidth = GridBagConstraints.REMAINDER;
		gbc.insets.bottom = 5;
		buttonPanel.add(cmdAdd, gbc);
		gbc.insets.top = 5;
		gbc.insets.bottom = 0;
		buttonPanel.add(cmdRemove, gbc);
		return buttonPanel;
	}

	public void setTitles(final String applicableTitle, final String availableTitle) {
		tblApplicable.setBorder(BorderFactory
				.createTitledBorder(applicableTitle));
		tblAvailable
				.setBorder(BorderFactory.createTitledBorder(availableTitle));
	}

	public void setLabels(final DisplayDescriptor labels, final String id) {
		tblApplicable.setLabels(labels, createApplicableTableID(id));
		tblAvailable.setLabels(labels, createAvailableTableID(id));
	}

	public void setData(final DataSet availableData, final DataSet applicableData) {
		tracker.clear();
		tblApplicable.setData(applicableData);
		tblAvailable.setData(availableData);
	}

	public void setDataMoveListener(final DataMoveListener dml) {
		dataMoveListener = dml;
	}

	protected void fireDataAdded(final Data rec) {
		if (dataMoveListener != null) {
			dataMoveListener
					.dataMoved(new DataMoveEvent(rec, DataMoveType.ADD));
		}
	}

	protected void fireDataRemoved(final Data rec) {
		if (dataMoveListener != null) {
			dataMoveListener.dataMoved(new DataMoveEvent(rec,
					DataMoveType.REMOVE));
		}
	}

	private String createApplicableTableID(final String id) {
		return id + "applicableTable";
	}

	private String createAvailableTableID(final String id) {
		return id + "availableTable";
	}


	public String getIdentifier() {
		return compID;
	}

	public void setIdentifier(final String s) {
		compID = s;
//		tblApplicable.setIdentifier(createApplicableTableID(s));
//		tblAvailable.setIdentifier(createAvailableTableID(s));
	}
}
